{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b31248a8",
   "metadata": {},
   "source": [
    "## 绑定运行时参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "90cf6ee9",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI, OpenAI\n",
    "openai_api_key = \"EMPTY\"\n",
    "openai_api_base = \"http://127.0.0.1:1234/v1\"\n",
    "model = ChatOpenAI(\n",
    "    openai_api_key=openai_api_key,\n",
    "    openai_api_base=openai_api_base,\n",
    "    temperature=0,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "735eb29e",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.runnables import RunnablePassthrough"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "fffdc026",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt = ChatPromptTemplate.from_messages(\n",
    "    [\n",
    "        (\n",
    "            \"system\",\n",
    "            \"用代数符号写出下面的方程，然后求解。 使用格式\\n\\n方程:...\\n解决方案:...\\n\\n\",\n",
    "        ),\n",
    "        (\"human\", \"{equation_statement}\"),\n",
    "    ]\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "1b0c305e",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "方程：x^3 + 7 = 34\n",
      "\n",
      "解决方案：将方程两边同时减去7，得到x^3 = 27。然后开立方根，得到x = 3。\n"
     ]
    }
   ],
   "source": [
    "chain = (\n",
    "    {\"equation_statement\": RunnablePassthrough()} | prompt | model | StrOutputParser()\n",
    ")\n",
    "\n",
    "print(chain.invoke(\"x的3次方加7等于34\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eb8857f1",
   "metadata": {},
   "source": [
    "## 在运行时配置链的内部结构\n",
    "\n",
    "通常，您可能想要尝试多种不同的做事方式，甚至向最终用户展示多种不同的做事方式。为了使这种体验尽可能简单，我们定义了两种方法。\n",
    "\n",
    "首先是 configurable_fields 方法。这允许您配置可运行的特定字段。\n",
    "\n",
    "其次，一个 configurable_alternatives 方法。使用此方法，您可以列出可以在运行时设置的任何特定可运行对象的替代方案。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7106f340",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain.prompts import PromptTemplate\n",
    "from langchain_core.runnables import ConfigurableField"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "37c75954",
   "metadata": {},
   "outputs": [],
   "source": [
    "model = model.configurable_fields(\n",
    "    temperature=ConfigurableField(\n",
    "        id=\"llm_temperature\",\n",
    "        name=\"LLM Temperature\",\n",
    "        description=\"The temperature of the LLM\",\n",
    "    )\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "444687c5",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='23')"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.invoke(\"生成一个随机的整数，不要回答其他任何内容\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "eb5b034a",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='23')"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.invoke(\"生成一个随机的整数，不要回答其他任何内容\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "f514de87",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "AIMessage(content='5')"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "model.with_config(configurable={\"llm_temperature\": 0.9}).invoke(\"生成一个随机的整数，不要回答其他任何内容\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "830fe030",
   "metadata": {},
   "source": [
    "## `@chain` decorator\n",
    "\n",
    "使用“@chain”装饰器创建一个可运行程序,您还可以通过添加 @chain 装饰器将任意函数变成链。这在功能上相当于包装在 RunnableLambda 中。\n",
    "\n",
    "通过正确跟踪您的链，这将具有提高可观察性的好处。对该函数内的可运行对象的任何调用都将被跟踪为嵌套子函数。\n",
    "\n",
    "它还允许您像任何其他可运行程序一样使用它，将其组合成链等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "931e4ecd",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.output_parsers import StrOutputParser\n",
    "from langchain_core.prompts import ChatPromptTemplate\n",
    "from langchain_core.runnables import chain"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "f2e06654",
   "metadata": {},
   "outputs": [],
   "source": [
    "prompt1 = ChatPromptTemplate.from_template(\"给我讲一个关于 {topic} 的故事\")\n",
    "prompt2 = ChatPromptTemplate.from_template(\"{story}\\n\\n对上面这个故事进行修改，让故事变得更加口语化和幽默有趣\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "682d3599",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_openai import ChatOpenAI, OpenAI\n",
    "openai_api_key = \"EMPTY\"\n",
    "openai_api_base = \"http://127.0.0.1:1234/v1\"\n",
    "model = ChatOpenAI(\n",
    "    openai_api_key=openai_api_key,\n",
    "    openai_api_base=openai_api_base,\n",
    "    temperature=0.8,\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "2bd7af8b",
   "metadata": {},
   "outputs": [],
   "source": [
    "@chain\n",
    "def custom_chain(text):\n",
    "    prompt_val1 = prompt1.invoke({\"topic\":text })\n",
    "    output1 = model.invoke(prompt_val1)\n",
    "    parsed_output1 = StrOutputParser().invoke(output1)\n",
    "    \n",
    "    chain2 = prompt2 | model | StrOutputParser()\n",
    "    return chain2.invoke({\"story\": parsed_output1})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "c715f9b2",
   "metadata": {},
   "outputs": [],
   "source": [
    "@chain\n",
    "def custom_chain2(text):\n",
    "    chain1 = prompt1 | model | StrOutputParser()\n",
    "    parsed_output1 = chain1.invoke({\"topic\":text })\n",
    "    \n",
    "    chain2 = prompt2 | model | StrOutputParser()\n",
    "    return chain2.invoke({\"story\": parsed_output1})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "501c749b",
   "metadata": {},
   "outputs": [],
   "source": [
    "from langchain_core.runnables import RunnablePassthrough\n",
    "\n",
    "@chain\n",
    "def custom_chain3(text):\n",
    "    chain = prompt1 | model | StrOutputParser()| {\"story\": RunnablePassthrough()} | prompt2 | model | StrOutputParser()\n",
    "    return chain.invoke({\"topic\":text })"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "ba287b65",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'老话说得好：“有志者事竟成”，有一个小故事特别能说明这个道理。一个家伙想爬一座高山，这山高得离谱，看着都让人头疼。但是他没被吓到，一心想着一定要登顶。他一试再试，摔了无数次跟头，但是他不气馁，每次跌倒就笑一笑，拍拍屁股继续往上爬。别人劝他放弃，说：“你看看这山这么高，怎么可能爬上去呢？”但他不听，坚持不懈地往上爬。最后，奇迹发生了！他终于登顶了！这个故事告诉我们，只要你有心，不怕失败，坚持到底，不论多困难的事都能成功！'"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "custom_chain.invoke(\"有志者事竟成\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "5cb805fb",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'阿比吉特是印度一个穷乡僻壤的娃儿，家里一贫如洗，连电、自来水、路都木有！但他可不服输，一心想读书改命。每天在河边寻摸纸张和棍子，自学读写。有一天，来了个城里人，看见阿比吉特在石头上写画，惊呆了。当得知他的处境和愿望后，这个人心动了，决定拉他一把。联系一个NGO（非政府组织），给了他奖学金让他去城里上学。\\n\\n凭着自己的努力和坚持，阿比吉特最终拿到工程学学位并在一家知名科技公司找到工作。但没忘本的他，现在致力于帮助像自己一样的穷孩子受教育。这个故事告诉我们，只要有决心和毅力，就能克服困难，实现梦想。'"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "custom_chain2.invoke(\"有志者事竟成\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "01f0fddc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'东汉初年，有一位名叫耿弇（音：yǎn）的小伙子从小就立志当兵保卫国家。他聪明伶俐，勤奋好学，尤其喜欢琢磨打仗的事儿。长大后，耿弇投奔了当时的名将刘秀，很快就被看重和提拔。\\n\\n有一天，刘秀派耿弇去收拾山东那边的土霸王张步。张步占着几座城池，兵强马壮，可不是好惹的。刚开始几次打仗，耿弇都输了，但他没灰心丧气，反而更坚定了必胜的决心。他一边训练部队、鼓舞士气，一边找张步的破绽和弱点。\\n\\n仔细观察了一段时间后，耿弇发现张步虽然实力强，但城里城外联系不紧密。于是，他决定各个击破，先拿下一个个小城池。\\n\\n首先，耿弇派了点人马假装攻打张步的大本营临淄城，吸引他的主力来救。当敌人出城后，耿弇立即带主力部队轻松夺下了空城的临淄城。接着，他又利用敌人分散的弱点，闪电般地攻占了一座又一座城池。\\n\\n等张步发现不对劲的时候，已经晚了，他不得不投降，山东也就此平定了。\\n\\n刘秀知道耿弇成功后非常高兴，直感叹：“有志气的人，啥事都能干成！”从此，这句话流传开来，激励着后来的勇士们勇往直前、永不放弃。'"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "custom_chain3.invoke(\"有志者事竟成\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "409a6e98",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.9"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
